package com.etime.shyauth.controller;

import com.etime.shyauth.config.BaseMessage;
import com.etime.shyauth.config.PojoMessage;
import com.etime.shyauth.dto.CurrentUser;
import com.etime.shyauth.dto.WXUser;
import com.etime.shyauth.pojo.*;
import com.etime.shyauth.services.interfaces.CurrentUserService;
import com.etime.shyauth.services.interfaces.SysUserRoleService;
import com.etime.shyauth.services.interfaces.SysUserService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.json.JsonParseException;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 登录服务
 */
@Api(tags = "用户登录服务")
@Controller
@ResponseBody
@RequestMapping("/login")
public class LoginController {

    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private CurrentUserService currentUserService;
    @Autowired
    private SysUserRoleService sysUserRoleService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Value("${appid}")
    private String appid;
    @Value("${secret}")
    private String secret;

    @ApiOperation("使用用户名和密码登录")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "username", value = "用户名"),
            @ApiImplicitParam(name = "userpwd", value = "密码")
    })
    @PostMapping("/byuser")
    public BaseMessage usernamelogin(String username, String userpwd) {
        BaseMessage msg = new BaseMessage();
        //根据用户名查询用户实体
        SysUser user = new SysUser();
        user.setUsername(username);
        //用户必须是可用状态
        user.setUserstate(1);
        List<SysUser> userlist = sysUserService.selectUserList(user);
        if (userlist != null && userlist.size() > 0) {
            SysUser u = userlist.get(0);
            //验证密码
            String pwd = DigestUtils.md5DigestAsHex(DigestUtils.md5DigestAsHex(userpwd.getBytes()).getBytes());
            if (pwd.equals(u.getUserpwd())) {
                /*验证成功
                1、封装关键数据：是否机构管理员，机构id，教师id，销售员id，角色列表
                2、将封装好的数据序列化后存入redis缓存；默认存放时间30分钟
                 */
                CurrentUser currentUser = dealusermsg(u);
                //保存数据到redis中
                String key = saveUserToReids(currentUser);
                msg.setCode(1);
                msg.setMsg(key);
            } else {
                msg.setCode(0);
                msg.setMsg("密码错误");
            }
        } else {
            //用户名不存在或者用户已被锁定
            msg.setCode(0);
            msg.setMsg("用户名不存在或者用户已被锁定");
        }
        return msg;
    }

    /**
     * 小程序端微信授权登录数据接口
     * 1、接收参数： code,nickname,头像图片地址（avatarUrl）
     * 2、程序逻辑：
     * 2.1首先使用code从微信端获取openid和unionid
     * 2.2根据unionid从数据库中查询用户；判断用户是否存在
     * 2.2.1 如果用户存在，则表示用户已经注册；将用户信息存入到redis中；
     * 并将token，openid，unionid等信息反馈到小程序。
     * 2.2.2 如果用户不存在，则注册用户；并将注册完成的用户信息保存到redis中；
     * 同样，将token，openid，unionid等信息反馈到小程序
     * <p>
     * 增加（wb） 判断用户是否是新用户，如果是则记录对应的销售人员。如果是老用户，如果没有销售人员，则新增销售人员（用户id）。
     */
    @ApiOperation("微信授权登录，点击授权进行操作")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "code", value = "用于交换openid的code"),
            @ApiImplicitParam(name = "nickName", value = "用户昵称"),
            @ApiImplicitParam(name = "avatarUrl", value = "用户头像地址"),
            @ApiImplicitParam(name = "saleruserid", value = "从属销售人员id")
    })
    @PostMapping("/bywx")
    // @CrossOrigin
    //String code, String nickName, String avatarUrl
    public PojoMessage<WXUser> unionidlogin(@RequestBody Map<String, Object> params) {
        String code = (String) params.get("code");
        //从属销售人员id
        Integer saleUserId = null;
        if (params.get("saleruserid") != null) {
            saleUserId = (Integer) params.get("saleruserid");
        }
        PojoMessage<WXUser> msg = new PojoMessage<>();
        //2.1首先使用code从微信端获取openid和unionid
        Map<String, String> map = getwxuserinfo(code);
        //System.out.println(map);
        //标记用户表中是否已经存储了unionid。
        // 如果有用户，但是没有unionid则将unionid更新到数据中；
        boolean flag = false;
        boolean isnewuser = false;
        if (map != null) {
            List<SysUser> userlist = null;
            String unionid = map.get("unionid");
            String openid = map.get("openid");
            if (unionid != null && !"".equals(unionid)) {
                //2.2根据unionid从数据库中查询用户
                userlist = selectUserByUnionid(unionid);
                //如果有unionid，但是数据库中没有存储unionid，
                //那么需要将unionid更新到数据库中
                if (userlist != null && userlist.size() > 0) {
                } else {
                    userlist = selectUserByOpenid(openid);
                    flag = true;
                }

            } else {
                //如果unionid不存在的情况，则使用openid
                userlist = selectUserByOpenid(openid);
            }
            //判断用户是否存在
            SysUser u = null;
            if (userlist != null && userlist.size() > 0) {
                //2.2.1 如果用户存在，则表示用户已经注册；将用户信息存入到redis中；
                //并将token，openid，unionid等信息反馈到小程序。
                u = userlist.get(0);
                //更新unionid到数据库中
                if (flag) {
                    u.setUnionid(unionid);
                    sysUserService.updateSysUserUnionId(u);
                }
                // 如果是老用户，如果该用户信息中没有销售人员userid，则为该用户 添加销售人员（用户id）。
                if (saleUserId != null && u.getSaleruserid() == null) {
                    u.setSaleruserid(saleUserId);
                    sysUserService.updateSysSaleUserId(u);
                }
            } else {
                // 2.2.2 如果用户不存在，则注册用户；并将注册完成的用户信息保存到redis中；
                // 同样，将token，openid，unionid等信息反馈到小程序
                SysUser user = new SysUser();
                user.setUserstate(1);
                user.setShowpic((String) params.get("avatarUrl"));
                user.setIsteacher(0);
                user.setIssaler(0);
                user.setNickname(filterEmoji((String) params.get("nickName")));
                user.setOpenid(openid);
                user.setUnionid(unionid);
                if (saleUserId != null) {
                    // 携带从属销售人员id 则记录对应的销售人员
                    user.setSaleruserid(saleUserId);
                }
                u = registUser(user);
                isnewuser = true;
            }
            //处理user对象，并记录到redis中
            if (u != null) {
                CurrentUser currentUser = dealusermsg(u);
                //保存数据到redis中
                String token = saveUserToReids2(currentUser);
                msg.setCode(1);
                msg.setMsg("授权登录成功");
                WXUser wxUser = new WXUser();
                wxUser.setOpenid(openid);
                wxUser.setUnionid(unionid);
                wxUser.setToken(token);
                msg.setData(wxUser);
            } else {
                msg.setCode(0);
                msg.setMsg("用不存在或者注册用户失败");
            }
        } else {
            //没有获取到用户数据
            msg.setCode(0);
            msg.setMsg("没有获取到微信用户数据");
        }
        if (isnewuser) {
            //如果是新用户，则返回code=2
            msg.setCode(2);
            msg.setMsg("新用户注册成功。");
        }
        return msg;
    }

    /**
     * 保存用户数据到redis中
     * key=token; 使用用户id+用户名+密码混合加密后的结果作为key
     * value=user
     * 保存时间：30min
     *
     * @param user
     * @return 返回需要客户端记录的token；也是记录到redis中的key。
     */
    private String saveUserToReids(CurrentUser user) {
        String key = DigestUtils.md5DigestAsHex(user.getUser().getUserid().toString().getBytes());
        //转换为json格式字符串存储
        ObjectMapper om = new ObjectMapper();
        String str;
        try {
            str = om.writeValueAsString(user);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            str = "";
        }
        stringRedisTemplate.boundValueOps(key).set(str);
        //redis缓存不过期
        //stringRedisTemplate.boundValueOps(key).expire(30, TimeUnit.MINUTES);
        return key;
    }

    /**
     * 保存用户数据到redis中
     * key=token; 使用用户id+用户名+密码混合加密后的结果作为key
     * value=user
     * 保存时间：不失效
     *
     * @param user
     * @return 返回需要客户端记录的token；也是记录到redis中的key。
     */
    private String saveUserToReids2(CurrentUser user) {
        String key = DigestUtils.md5DigestAsHex(user.getUser().getUserid().toString().getBytes());
        //转换为json格式字符串存储
        ObjectMapper om = new ObjectMapper();
        String str;
        try {
            str = om.writeValueAsString(user);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            str = "";
        }
        stringRedisTemplate.boundValueOps(key).set(str);
        //stringRedisTemplate.boundValueOps(key).expire(30, TimeUnit.MINUTES);
        return key;
    }

    /**
     * 处理用户信息
     * 1、判断用户是否是教师；如果是教师，则从教师表中查询教师id和所属机构id
     * 2、判断用户是否是销售员；如果是销售员，则从销售人员表中查询出salerid
     * 3、判断用户是否是机构管理员；如果是，则从教学机构中查询出机构id
     * 4、查询出用户所具有的角色列表
     *
     * @param user 用户信息
     * @return 封装后的用户信息
     */
    private CurrentUser dealusermsg(SysUser user) {
        CurrentUser currentUser = new CurrentUser();
        //缓存中不保存密码
        user.setUserpwd("");
        currentUser.setUser(user);
        //如果是教师
        if (user.getIsteacher() != null && user.getIsteacher() == 1) {
            Teacher tec = currentUserService.selectTec(user.getUserid());
            if (tec != null) {
                currentUser.setTecid(tec.getTecid());
                currentUser.setOrgid(tec.getTecorgid());
            }
        }
        //如果是销售人员
        if (user.getIssaler() != null && user.getIssaler() == 1) {
            Saler saler = currentUserService.selectSaler(user.getUserid());
            if (saler != null) {
                currentUser.setSalerid(saler.getSalerid());
                currentUser.setDeptid(saler.getDeptid());
            }
        }
        //判断是否是机构管理员
        Organization org = currentUserService.selectorg(user.getUserid());
        if (org != null) {
            currentUser.setOrgid(org.getOrgid());
        }
        //获取用户的角色列表
        List<SysUserRole> rolelist = sysUserRoleService.selectSysUserRoleList(user.getUserid());
        List<Integer> roleids = new ArrayList<>();
        if (rolelist != null) {
            for (SysUserRole role : rolelist) {
                roleids.add(role.getRoleid());
            }
        }
        currentUser.setRoleids(roleids);
        return currentUser;
    }


    /**
     * 使用code从微信端交换openid
     *
     * @param code
     * @return
     */
    private Map<String, String> getwxuserinfo(String code) {
        String WX_URL = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appid + "&secret=" + secret + "&js_code="
                + code + "&grant_type=authorization_code";
        String rtnvalue = GET(WX_URL);//获取到的是一个json字符串。
        System.out.println(rtnvalue);
        //解析json字符串
        ObjectMapper mapper = new ObjectMapper();
        Map<String, String> map = null;
        try {
            map = mapper.readValue(rtnvalue, Map.class);
        } catch (JsonParseException e) {
            e.printStackTrace();
        } catch (JsonMappingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return map;
    }

    /**
     * 发起微信服务发送get请求的方法。
     *
     * @param url
     * @return
     */
    private String GET(String url) {
        String result = "";
        BufferedReader in = null;
        InputStream is = null;
        InputStreamReader isr = null;
        try {
            URL realUrl = new URL(url);
            URLConnection conn = realUrl.openConnection();
            conn.connect();
            Map<String, List<String>> map = conn.getHeaderFields();
            is = conn.getInputStream();
            isr = new InputStreamReader(is);
            in = new BufferedReader(isr);
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            // 异常记录
            e.printStackTrace();
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
                if (is != null) {
                    is.close();
                }
                if (isr != null) {
                    isr.close();
                }
            } catch (Exception e2) {
                // 异常记录
                e2.printStackTrace();
            }
        }
        return result;
    }

    /**
     * 根据unionid查询用户信息；
     *
     * @param unionid 用户的unionid
     * @return
     */
    private List<SysUser> selectUserByUnionid(String unionid) {
        SysUser user = new SysUser();
        user.setUnionid(unionid);
        List<SysUser> list = sysUserService.selectUserList(user);
        return list;
    }

    /**
     * 根据openid查询用户信息；
     *
     * @param openid 用户的opendid
     * @return
     */
    private List<SysUser> selectUserByOpenid(String openid) {
        SysUser user = new SysUser();
        user.setOpenid(openid);
        List<SysUser> list = sysUserService.selectUserList(user);
        return list;
    }

    /**
     * 注册新用户到数据库
     *
     * @param user
     * @return
     */
    private SysUser registUser(SysUser user) {
        int rtn = sysUserService.insertSysUser(user);
        if (rtn > 0) {
            return user;
        } else {
            return null;
        }
    }

    /**
     * 替换用户昵称中的表情字符
     *
     * @param source
     * @return
     */
    private static String filterEmoji(String source) {
        if (source != null) {
            final Pattern emoji = Pattern.compile("[\ud83c\udc00-\ud83c\udfff]|[\ud83d\udc00-\ud83d\udfff]|[\u2600-\u27ff]", Pattern.UNICODE_CASE | Pattern.CASE_INSENSITIVE);
            Matcher emojiMatcher = emoji.matcher(source);
            if (emojiMatcher.find()) {
                source = emojiMatcher.replaceAll("");
                return source;
            }
            return source;
        }
        return source;
    }
}
